import java.io.*;
import java.net.*;
import java.util.LinkedList;

public class Table //implements Runnable
{
	private static final int PORT = 5564;
//  private static int active_players;
    private static int players = 0;
	private Card karty[];
	private static int last_card;
	private static int karty_graczy;
	Deck talia;
	private final int max_graczy = 4;
	private static ObjectInputStream ois[] = null;
    private static ObjectOutputStream oos[] = null;
	private static ServerSocket socket_serwera = null;
	private static Socket socket_klienta[] = null;
	private static int check = 0;
	private static String nazwy_graczy[];
	private static boolean exit = false;
	
	public Table()
	{
		try
		{
			socket_serwera = new ServerSocket(PORT);
		}
		catch (IOException ioe)
		{
			System.out.println("Nie mozna sluchac portu!");
			System.exit(-1);
		}
		
		socket_klienta = new Socket [max_graczy];
		ois = new ObjectInputStream [max_graczy];
		oos = new ObjectOutputStream [max_graczy];
		talia = new Deck();
		karty = new Card[20];
		last_card = 0;
		nazwy_graczy = new String[max_graczy];
	}
	
	public static void main(String args[])
	{
		Table serwer = new Table();
		serwer.socketListener();
	}
	
	public void socketListener()
	{
		Thread players_threads[] = new Thread [max_graczy];
		while (players < max_graczy)
		{
			try
			{
				socket_klienta[players] = socket_serwera.accept();
		    	ois[players] = new ObjectInputStream(socket_klienta[players].getInputStream());
		    	oos[players] = new ObjectOutputStream(socket_klienta[players].getOutputStream());

				for (int i = 0; i < 5; ++i)
					send_card_to_player(players, false);				//wyslanie kart do gracza

				players_threads[players] = new Thread(new Runnable()
				{
					public void run()
					{
						int n_socket = 0;
						boolean wymienil = false;
						
						try
						{
							n_socket = Integer.parseInt(Thread.currentThread().getName());
						}
						catch (NumberFormatException nfe)
						{
							System.out.println("Nieprawidlowa nazwa watku!");
						}
							
						String linia = new String("");
						
				    	while (linia != null)
					    {	     
				    		try
							{
								linia = (String) ois[n_socket].readObject();
										
							    try
								{
									Thread.sleep(150);
								}
							    catch (InterruptedException e)
								{
									System.out.println(e.getMessage());
								}
							    		
							    if (linia != null)
							    {	
							    	if (linia.contains("change"))
							       	{
							    		if (!wymienil)
							    		{
							    			get_card_from_player(n_socket);
							    			send_card_to_player(n_socket, wymienil);
							    		}
							    		else
							    			send_card_to_player(n_socket, wymienil);
							        	wymienil = true;
							        }
							       	else if (linia.contains("check"))
							       	{
							       		System.out.println("Otrzymano potwierdzenie sprawdzenia!");
							     		get_hand_from_player(n_socket);
							     		System.out.println("Odebrano karty od gracza!");
							     		++check;
							     		System.out.println(check);
							     		while (check != -1)
							     			Thread.sleep(2000);
							     		if (check == -1)
							     			linia = null;
							       	}
							        else
							        	oos[n_socket].writeObject("Nie rozpoznano polecenia!");	
								   
							        try
									{
										Thread.sleep(150);
									}
								    catch (InterruptedException e)
									{
							 			System.out.println(e.getMessage());
									}
							    }
							}
							catch (ClassNotFoundException cne)
							{
								System.out.println(cne.getMessage());
							}
							catch (IOException ioe)
							{
								linia = null;
							    System.out.println("Nastapil blad w komunikacji!");
							} 
				    		catch (InterruptedException e)
							{
								e.printStackTrace();
							}
					    }
					}
				});
				
				players_threads[players].setName(Integer.toString(players));
		    	players_threads[players].start();
		    	++players;
			}
			catch (IOException ioe)
			{
				System.out.println("Nie mozna zaakceptowac polaczenia!");
				System.exit(-1);
			}
		}
		Thread watek_glowny = new Thread (new Runnable()
		{
			public void run()
			{
				while (!exit)
				{
					try
					{
						while (check != max_graczy)
							Thread.sleep(1000);
						if (check == max_graczy)
							evaluated();
						exit = true;
					}
					catch (InterruptedException ie)
					{
						System.out.println(ie.getMessage());
					}
				}
			}
		});
		watek_glowny.start();
	}

	public void finalize()
	{
		System.out.println("Wychodz�!");
		try
		{
			for (int i = 0; i < players; ++i)
				socket_klienta[i].close();
			socket_serwera.close();
		}
		catch (IOException ioe)
		{
			System.out.println("Could not close.");
			System.exit(-1);
		}
	}
	
	private void send_card_to_player(int liczba, boolean wymienil)
	{
		try
		{
			if (!wymienil)
			{
				//talia.zwroc_karte(last_card).set_owner("gracz" + liczba);
				Card temp = new Card(talia.zwroc_karte(last_card).pokaz_znak(),
									talia.zwroc_karte(last_card).pokaz_wartosc());
				temp.set_owner("gracz" + liczba);
				//oos[liczba].writeObject(talia.zwroc_karte(last_card));
				oos[liczba].writeObject(temp);
				kolejna_karta();
			}
			else
				oos[liczba].writeObject(null);
		} 
		catch (IOException e)
		{
			System.out.println("Nie udalo sie wyslac!");
		}
	}
	
	private Card get_card_from_player(int liczba)
	{
		Card temp = new Card();
		try
		{
			temp = (Card) ois[liczba].readObject();
			kolejne_karty_graczy(1);
			return temp;
		} 
		catch (ClassNotFoundException e)
		{
			System.out.println(e.getMessage());
		} 
		catch (IOException e)
		{
			System.out.println(e.getMessage());
		}
		
		return null;
	}
	
	private void get_hand_from_player(int liczba)
	{
		int zakres = karty_graczy + 5;
		for (int i = karty_graczy; i < zakres /*(karty_graczy + 5) && (karty_graczy < 52)*/; ++i)
		{
			try
			{
				karty[i] = (Card) ois[liczba].readObject();
				kolejne_karty_graczy(1);
			} 
			catch (ClassNotFoundException e)
			{
				e.printStackTrace();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
	
	private void evaluated()
	{
		sortuj();
		String wynik = "";
		int ranking[] = new int [players];
		int indeks = 0;
		
		for (int i = 0; i < players * 5; i += 5)
		{			
			if (czy_poker(i))
				ranking[indeks] = 1;
			else if (czy_czworka(i))
				ranking[indeks] = 2;
			else if (czy_full(i))
				ranking[indeks] = 3;
			else if (czy_kolor(i))
				ranking[indeks] = 4;
			else if (czy_strit(i))
				ranking[indeks] = 5;
			else if (czy_trojka(i))
				ranking[indeks] = 6;
			else if (czy_dwie_pary(i))
				ranking[indeks] = 7;
			else if (czy_para(i))
				ranking[indeks] = 8;
			else
				ranking[indeks] = 9;

			++indeks;
		}
		
		int min = 9;
		LinkedList<Integer> lista = new LinkedList<Integer>();
		
		for (int i = 0; i < players; ++i)
			if (min > ranking[i])
				min = ranking[i];
		
		for (int i = 0; i < players; ++i)
			if (min == ranking[i])
				lista.add(i+1);
		
		if (lista.size() == 1)
			wynik = karty[(lista.get(0) * 5) - 1].wlasciciel();
		else
		{
			if (min == 2 || min == 3 || min == 6)
			{
				int max = 0;
				for (int i = 0; i < lista.size(); ++i)
					if (karty[(lista.get(i) * 5) - 3].rozpoznaj_wartosc() > max)
						max = karty[(lista.get(i)*5)-3].rozpoznaj_wartosc();
				
				for (int i = 0; i < lista.size(); ++i)
					if (karty[(lista.get(i) * 5) -3].rozpoznaj_wartosc() == max)
						wynik += (karty[(lista.get(i) * 5) - 3].wlasciciel() + " ");
			}
			else if (min == 4 || min == 9 || min == 1 || min == 5)
			{
				int max = karty[(lista.get(0) * 5) - 1].rozpoznaj_wartosc(); 
				for (int i = 1; i < lista.size(); ++i)
					if (max < karty[(lista.get(i) * 5) - 1].rozpoznaj_wartosc())
						max = karty[(lista.get(i) * 5) - 1].rozpoznaj_wartosc();
				
				int licznik = 0;
				for (int i = 0; i < lista.size(); ++i)
					if (karty[(lista.get(i) * 5) - 1].rozpoznaj_wartosc() == max)
					{
						++licznik;
						min = (lista.get(i) * 5) - 1;
					}
				
				if (licznik == 1)
					wynik = karty[min].wlasciciel();
				else
				{
					boolean exit = false;
					licznik = 0;
					int j = 0;
					
					while (j < 4 && !exit)
					{
						max = 0;
						int i = 0;
						licznik = 0;
						++j;

						do
						{
							licznik = 0;
							
							for (int k = 0; k < lista.size(); ++k)
								if (karty[(lista.get(k) * 5) - j].rozpoznaj_wartosc() > max)
									max = karty[(lista.get(i) * 5) - j].rozpoznaj_wartosc();
							
							for (int k = 0; k < lista.size(); ++k)
								if (karty[(lista.get(k) * 5) - j].rozpoznaj_wartosc() == max)
									++licznik;
							
							if (licznik == 1)
							{
								exit = true;
								i = lista.size();
							}
							
							++i;
						} while (i < lista.size());
					}
					
					for (int i = 0; i < lista.size(); ++i)
						if (karty[(lista.get(i) * 5) - j].rozpoznaj_wartosc() == max)
							wynik += (karty[(lista.get(i) * 5) -1].wlasciciel() + " ");
				}
			}
			else if (min == 7 || min == 8)
			{
				int max = 0;
				int temp = 0;
					
				LinkedList<Integer> list = new LinkedList<Integer>();
				for (int m = 1; m < 4; ++m)
					for (int k = 0; k < lista.size(); ++k)
						if (karty[(lista.get(k) * 5) - m].compare(
								karty[(lista.get(k) * 5) - (m+1)]))
						{
							list.add(karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc());
							if (karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc() > max)
								max = karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc();
						}
				
				int licznik = 0;
				for (int m = 1; m < 5; ++m)
					for (int k = 0; k < lista.size(); ++k)
						if (karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc() == max)
							++licznik;
							
				if (licznik != 1)
				{
					for (int m = 1; m < 5; ++m)
						for (int k = 0; k < lista.size(); ++k)
							for (int l = 0; l < list.size(); ++l)
								if (karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc() !=
									list.get(l))
								{
									max = karty[(lista.get(k) * 5) - m].rozpoznaj_wartosc();
									temp = m;
								}
				}
				
				for (int k = 0; k < lista.size(); ++k)
					if (karty[(lista.get(k) * 5) - temp].rozpoznaj_wartosc() == max)
						wynik += (karty[(lista.get(k) * 5) - temp].wlasciciel() + " ");
			}
		}
		
		if (check_game() != null)
			wynik = check_game();
		send_results("Wygrywaj�cy: " + wynik);
		check = -1;
	}
	
	private String check_game()
	{
		String oszustwo = null;
		for (int j = 0; j < (max_graczy * 5); ++j)
			for (int i = 0; i < (max_graczy * 5); ++i)
			{
				if (i != j && oszustwo == null)
				{
					if (karty[i].compare(karty[j]))
						if (karty[i].pokaz_znak().equals(karty[j].pokaz_znak()))
							oszustwo = new String("Wykryto oszustwo!");
				}
			}
		return oszustwo;
	}
	
	private void send_results(String wynik)
	{
		try
		{
			for (int i = 0; i < max_graczy; ++i)
				oos[i].writeObject(wynik);
		} catch (IOException e)
		{
			System.out.println("Nie udalo sie wyslac!");
		}
	}
	
	private void sortuj()
	{
		for (int i = 0; i < (players * 5) -1; i+= 5)
			for (int k = 0; k < 5; ++k)
				for (int j = i; j < i + 4; ++j)
					if (karty[j].rozpoznaj_wartosc() > karty[j+1].rozpoznaj_wartosc())
						karty[j].zamien(karty[j+1]);
	}
	
	private boolean czy_poker(int i)
	{
		if (czy_podobne(i, true) == 4 && czy_taki_sam_kolor(i) == 4)
			return true;
		
		return false;
	}
	
	private boolean czy_czworka(int i)
	{
		int pomocnicza = 0;
		if (czy_podobne(i, false) != 3)
			return false;
		
		if (!(karty[i].compare(karty[i + 2])))
			pomocnicza = i;
		else
			pomocnicza = i + 4;
		
		for (int j = i; j < i + 5; ++j)
			if (j != pomocnicza && karty[pomocnicza].compare(karty[j]))
				return false;
		
		return true;
	}
	
	private boolean czy_full(int i)
	{
		int pomocnicza = 0;
		if (czy_podobne(i, false) != 3)
			return false;
			
		if (!(karty[i].compare(karty[i + 2])))
			pomocnicza = i;
		else
			pomocnicza = i + 4;
		
		for (int j = i; j < i + 5; ++j)
		{
			if (j != pomocnicza && karty[pomocnicza].compare(karty[j]))
				return true;
		}
		
		return false;
	}
	
	private boolean czy_kolor(int i)
	{
		if (czy_taki_sam_kolor(i) == 4)
			return true;
		
		return false;
	}
	
	private boolean czy_strit(int i)
	{	
		if (czy_podobne(i, true) == 4)
			return true;
		
		return false;
	}
	
	private boolean czy_trojka(int i)
	{
		if (czy_podobne(i, false) != 2)
			return false;
		
		if (karty[i].compare(karty[i + 2]) 
			|| karty[i].compare(karty[i + 2]))
			return true;
		
		return false;
	}
	
	private boolean czy_dwie_pary(int i)
	{
		if (czy_podobne(i, false) == 2)
				return true;
		
		return false;
	}
	
	private boolean czy_para(int i)
	{
		if (czy_podobne(i, false) == 1)
			return true;
		
		return false;
	}
	
	private int czy_podobne(int i, boolean czy_rosnaco)
	{
		int pomocnicza = 0;
		
		if (!czy_rosnaco)
		{
			for (int j = i; j < i + 4; ++j)
				if (karty[j].compare(karty[j+1]))
					++ pomocnicza;
		}
		else
			for (int j = i; j < i + 4; ++j)
				if (karty[j].rozpoznaj_wartosc() + 1 == karty[j+1].rozpoznaj_wartosc())
					++pomocnicza;
					
		return pomocnicza;
	}
	
	private int czy_taki_sam_kolor(int i)
	{
		int pomocnicza = 0;
		
		for (int j = i; j < i + 4; ++j)
			if (karty[j].pokaz_znak().equals(karty[j+1].pokaz_znak()))
				++pomocnicza;
		
		return pomocnicza;
	}
	
	private void kolejna_karta()
	{
		if (last_card > -1 && last_card < 52)
			++last_card;
	}
	
	private void kolejne_karty_graczy(int i)
	{
		if (i == 1 || i == 5)
			if ((karty_graczy + i)  < 52)
				karty_graczy += i;
	}
}